module gpio_top_apb(
  input         clock,
  input         reset,
  input  [31:0] in_paddr,
  input         in_psel,
  input         in_penable,
  input  [2:0]  in_pprot,
  input         in_pwrite,
  input  [31:0] in_pwdata,
  input  [3:0]  in_pstrb,
  output        in_pready,
  output [31:0] in_prdata,
  output        in_pslverr,

  output [15:0] gpio_out,
  input  [15:0] gpio_in,
  output [7:0]  gpio_seg_0,
  output [7:0]  gpio_seg_1,
  output [7:0]  gpio_seg_2,
  output [7:0]  gpio_seg_3,
  output [7:0]  gpio_seg_4,
  output [7:0]  gpio_seg_5,
  output [7:0]  gpio_seg_6,
  output [7:0]  gpio_seg_7
);
  // GPIO: 0x1000_2000~0x1000_200f

  // ----------------------------------------------
  // |地址	|作用                                 |
  // ----------------------------------------------
  // |0x0	|16位数据, 分别驱动16个LED灯          |
  // |0x4	|16位数据, 分别获得16个拨码开关的状态 |
  // |0x8	|32位数据, 其中每4位驱动1个7段数码管  |
  // |0xc	|保留                                 |
  // ----------------------------------------------

  localparam GPIO_BASE    = 32'h1000_2000;
  localparam ADDR_LED     = GPIO_BASE + 32'd0;
  localparam ADDR_SWITCH  = GPIO_BASE + 32'd4;
  localparam ADDR_SEG     = GPIO_BASE + 32'd8;

  wire in_valid;

  reg [15:0] LED;
  reg [15:0] SWITCH;
  reg [31:0] SEG;

  reg        pready;
  reg [31:0] prdata;

  reg [7:0]  seg_0;
  reg [7:0]  seg_1;
  reg [7:0]  seg_2;
  reg [7:0]  seg_3;
  reg [7:0]  seg_4;
  reg [7:0]  seg_5;
  reg [7:0]  seg_6;
  reg [7:0]  seg_7;


  assign in_valid = in_psel && in_penable;

  assign in_pready = pready;
  assign in_prdata = prdata;

  // write into LED
  always @(posedge clock or posedge reset) begin
    if (reset) LED <= 16'd0;
    else if (in_valid && in_pwrite && (in_paddr == ADDR_LED)) begin
      case (in_pstrb)
        4'b0001: LED[ 7:0] <= in_pwdata[7:0];
        4'b0010: LED[15:8] <= in_pwdata[15:8];
        4'b0011: LED[15:0] <= in_pwdata[15:0];
        default: LED       <= LED;
      endcase
    end
  end

  // write into SEG
  always @(posedge clock or posedge reset) begin
    if (reset) SEG <= 32'd0;
    else if (in_valid && in_pwrite && (in_paddr == ADDR_SEG)) begin
      case (in_pstrb)
        4'b0001: SEG[ 7: 0] <= in_pwdata[ 7: 0];
        4'b0010: SEG[15: 8] <= in_pwdata[15: 8];
        4'b0100: SEG[23:16] <= in_pwdata[23:16];
        4'b1000: SEG[31:24] <= in_pwdata[31:24];
        4'b0011: SEG[15: 0] <= in_pwdata[15: 0];
        4'b1100: SEG[31:16] <= in_pwdata[31:16];
        4'b1111: SEG        <= in_pwdata;
        default: SEG        <= SEG;
      endcase
    end
  end

  // write into SWITCH
  always @(posedge clock or posedge reset) begin
    if (reset) SWITCH <= 16'd0;
    else SWITCH <= gpio_in;
  end

  // ready
  always @(posedge clock or posedge reset) begin
    if (reset) pready <= 1'b0;
    else if (pready) pready <= 1'b0;
    else if (in_valid && !pready) pready <= 1'b1;
  end

  // read
  always @(posedge clock or posedge reset) begin
    if (reset) prdata <= 32'd0;
    else if (in_valid && !in_pwrite) begin
      case (in_paddr)
        ADDR_LED:    prdata <= {16'd0, LED};
        ADDR_SWITCH: prdata <= {16'd0, SWITCH};
        ADDR_SEG:    prdata <= SEG;
      endcase
    end
  end

  always @(*) begin
    case (SEG[3:0])
      //             abcdefgh;
      0 : seg_0 = 8'b11111100;
      1 : seg_0 = 8'b00001100;
      2 : seg_0 = 8'b11011010;
      3 : seg_0 = 8'b11110010;
      4 : seg_0 = 8'b01100110;
      5 : seg_0 = 8'b10110110;
      6 : seg_0 = 8'b10111110;
      7 : seg_0 = 8'b11100000;
      8 : seg_0 = 8'b11111110;
      9 : seg_0 = 8'b11110110;
      10: seg_0 = 8'b11101110;
      11: seg_0 = 8'b00111110;
      12: seg_0 = 8'b10011100;
      13: seg_0 = 8'b01111010;
      14: seg_0 = 8'b11011110;
      15: seg_0 = 8'b10001110;
    endcase
  end

  always @(*) begin
    case (SEG[7:4])
      //             abcdefgh;
      0 : seg_1 = 8'b11111100;
      1 : seg_1 = 8'b00001100;
      2 : seg_1 = 8'b11011010;
      3 : seg_1 = 8'b11110010;
      4 : seg_1 = 8'b01100110;
      5 : seg_1 = 8'b10110110;
      6 : seg_1 = 8'b10111110;
      7 : seg_1 = 8'b11100000;
      8 : seg_1 = 8'b11111110;
      9 : seg_1 = 8'b11110110;
      10: seg_1 = 8'b11101110;
      11: seg_1 = 8'b00111110;
      12: seg_1 = 8'b10011100;
      13: seg_1 = 8'b01111010;
      14: seg_1 = 8'b11011110;
      15: seg_1 = 8'b10001110;
    endcase
  end

  always @(*) begin
    case (SEG[11:8])
      //             abcdefgh;
      0 : seg_2 = 8'b11111100;
      1 : seg_2 = 8'b00001100;
      2 : seg_2 = 8'b11011010;
      3 : seg_2 = 8'b11110010;
      4 : seg_2 = 8'b01100110;
      5 : seg_2 = 8'b10110110;
      6 : seg_2 = 8'b10111110;
      7 : seg_2 = 8'b11100000;
      8 : seg_2 = 8'b11111110;
      9 : seg_2 = 8'b11110110;
      10: seg_2 = 8'b11101110;
      11: seg_2 = 8'b00111110;
      12: seg_2 = 8'b10011100;
      13: seg_2 = 8'b01111010;
      14: seg_2 = 8'b11011110;
      15: seg_2 = 8'b10001110;
    endcase
  end

  always @(*) begin
    case (SEG[15:12])
      //            abcdefgh;
      0 : seg_3 = 8'b11111100;
      1 : seg_3 = 8'b00001100;
      2 : seg_3 = 8'b11011010;
      3 : seg_3 = 8'b11110010;
      4 : seg_3 = 8'b01100110;
      5 : seg_3 = 8'b10110110;
      6 : seg_3 = 8'b10111110;
      7 : seg_3 = 8'b11100000;
      8 : seg_3 = 8'b11111110;
      9 : seg_3 = 8'b11110110;
      10: seg_3 = 8'b11101110;
      11: seg_3 = 8'b00111110;
      12: seg_3 = 8'b10011100;
      13: seg_3 = 8'b01111010;
      14: seg_3 = 8'b11011110;
      15: seg_3 = 8'b10001110;
    endcase
  end

  always @(*) begin
    case (SEG[19:16])
      //            abcdefgh;
      0 : seg_4 = 8'b11111100;
      1 : seg_4 = 8'b00001100;
      2 : seg_4 = 8'b11011010;
      3 : seg_4 = 8'b11110010;
      4 : seg_4 = 8'b01100110;
      5 : seg_4 = 8'b10110110;
      6 : seg_4 = 8'b10111110;
      7 : seg_4 = 8'b11100000;
      8 : seg_4 = 8'b11111110;
      9 : seg_4 = 8'b11110110;
      10: seg_4 = 8'b11101110;
      11: seg_4 = 8'b00111110;
      12: seg_4 = 8'b10011100;
      13: seg_4 = 8'b01111010;
      14: seg_4 = 8'b11011110;
      15: seg_4 = 8'b10001110;
    endcase
  end

  always @(*) begin
    case (SEG[23:20])
      //             abcdefgh;
      0 : seg_5 = 8'b11111100;
      1 : seg_5 = 8'b00001100;
      2 : seg_5 = 8'b11011010;
      3 : seg_5 = 8'b11110010;
      4 : seg_5 = 8'b01100110;
      5 : seg_5 = 8'b10110110;
      6 : seg_5 = 8'b10111110;
      7 : seg_5 = 8'b11100000;
      8 : seg_5 = 8'b11111110;
      9 : seg_5 = 8'b11110110;
      10: seg_5 = 8'b11101110;
      11: seg_5 = 8'b00111110;
      12: seg_5 = 8'b10011100;
      13: seg_5 = 8'b01111010;
      14: seg_5 = 8'b11011110;
      15: seg_5 = 8'b10001110;
    endcase
  end

  always @(*) begin
    case (SEG[27:24])
      //            abcdefgh;
      0 : seg_6 = 8'b11111100;
      1 : seg_6 = 8'b00001100;
      2 : seg_6 = 8'b11011010;
      3 : seg_6 = 8'b11110010;
      4 : seg_6 = 8'b01100110;
      5 : seg_6 = 8'b10110110;
      6 : seg_6 = 8'b10111110;
      7 : seg_6 = 8'b11100000;
      8 : seg_6 = 8'b11111110;
      9 : seg_6 = 8'b11110110;
      10: seg_6 = 8'b11101110;
      11: seg_6 = 8'b00111110;
      12: seg_6 = 8'b10011100;
      13: seg_6 = 8'b01111010;
      14: seg_6 = 8'b11011110;
      15: seg_6 = 8'b10001110;
    endcase
  end

  always @(*) begin
    case (SEG[31:28])
      //             abcdefgh;
      0 : seg_7 = 8'b11111100;
      1 : seg_7 = 8'b00001100;
      2 : seg_7 = 8'b11011010;
      3 : seg_7 = 8'b11110010;
      4 : seg_7 = 8'b01100110;
      5 : seg_7 = 8'b10110110;
      6 : seg_7 = 8'b10111110;
      7 : seg_7 = 8'b11100000;
      8 : seg_7 = 8'b11111110;
      9 : seg_7 = 8'b11110110;
      10: seg_7 = 8'b11101110;
      11: seg_7 = 8'b00111110;
      12: seg_7 = 8'b10011100;
      13: seg_7 = 8'b01111010;
      14: seg_7 = 8'b11011110;
      15: seg_7 = 8'b10001110;
    endcase
  end

  assign gpio_out   = LED;

  assign gpio_seg_0 = ~seg_0;
  assign gpio_seg_1 = ~seg_1;
  assign gpio_seg_2 = ~seg_2;
  assign gpio_seg_3 = ~seg_3;
  assign gpio_seg_4 = ~seg_4;
  assign gpio_seg_5 = ~seg_5;
  assign gpio_seg_6 = ~seg_6;
  assign gpio_seg_7 = ~seg_7;

endmodule
